/*
* Implement all operations related to files
*/

#include "../inc/operate_file.h"
#include "../inc/log.h"


/* check whether file is empty */
int is_file_empty(const char *filename) {
    struct stat filestat;
    if (stat(filename, &filestat) < 0) {
        perror("stat");
        return -1;
    }
    return filestat.st_size == 0;
}

/*check whether file has extension */
int has_file_extension(const char *filename) {
    const char *dot = strrchr(filename, '.');
    return dot && dot != filename && *(dot + 1) != '\0';
}

/* getting the file suffix */
int get_file_suffix(const char *filename, char *suffix) {
    char *dot = strrchr(filename, '.');
    // has file extension
    if(dot && dot != filename && *(dot + 1) != '\0'){
        if (strlen(dot) < MAX_SUFFIX) {
            strcpy(suffix, dot + 1);
            return 1;
        }
    }
    return 0;
}

/* check whether is a c file */
int is_cfile(const char *filename){
    char suffix[MAX_SUFFIX];
    if(!get_file_suffix(filename, suffix)) return 0;
    return (strcmp(suffix, "c")== 0 || strcmp(suffix, "h")== 0);
}

/* check whether is a dir */
int is_dir(char *path) {
    struct stat st;
    stat(path, &st);
    if (S_ISDIR(st.st_mode)) {
        return 1;
    }else if(S_ISREG(st.st_mode)) {
        return 2;
    }
    return 0;
}

/* getting file prefix */
int get_file_prefix(char *filename, char *prefix){
    prefix = strtok(filename, ".");
    if (prefix) return 1;
    return 0;
}

/* getting filename without path */
int get_filename_without_path(const char* path, char *filename) {
    char* sep = strrchr(path, '/'); 
    if(sep && sep != path && *(sep + 1) != '\0'){
        if (sep != NULL) {
            strcpy(filename, sep + 1); 
            return 1;
        }
    }
    return 0;
}

/* getting all c files name and path */
void list_files(const char *path, const char* dest_dir, Queue *q) {
    DIR *d_dir, *dir;
    if ((d_dir = opendir(dest_dir)) == NULL) mkdir(dest_dir, 0755);
    closedir(d_dir);
    struct dirent *entry;
    if ((dir = opendir(path)) == NULL) {
        _log(NONE, __FILE__, "%s does not exist.", path);
        return;
    }
 
    while ((entry = readdir(dir)) != NULL) {
        // ignore current directory and parent directory
        if (strcmp(entry->d_name, ".") == 0 || strcmp(entry->d_name, "..") == 0) {
            continue;
        }
        if (entry->d_type == DT_DIR) {
            char *full_path = (char*)malloc(sizeof(char) * MAX_FILEPATH);
            snprintf(full_path, sizeof(char) * MAX_FILEPATH, "%s/%s", path, entry->d_name);
            char dest_sub_dir[MAX_FILEPATH];
            snprintf(dest_sub_dir, sizeof(dest_sub_dir), "%s/%s", dest_dir, entry->d_name);
            mkdir(dest_sub_dir, 0755);
            list_files(full_path, dest_sub_dir, q);
            free(full_path);
        }
        else if(entry->d_type == DT_REG){
            char *full = (char*)malloc(sizeof(char) * MAX_FILEPATH);
            snprintf(full, sizeof(char) * MAX_FILEPATH, "%s/%s", path, entry->d_name);
            char *dest_file = (char*)malloc(sizeof(char) * MAX_FILEPATH);
            snprintf(dest_file, sizeof(char) * MAX_FILEPATH, "%s/%s.html", dest_dir, entry->d_name);
            if(is_cfile(full) && !is_file_empty(full)){
                Pair *pair = (Pair*)malloc(sizeof(Pair));
                pair->first = full;
                pair->second = dest_file;
                queue_push(q, pair);
            }else{
                free(full);
                free(dest_file);
            }
        }
    }
    closedir(dir);
}

/* Copy the folder to the specified path */
void copy_dir(const char* source_dir, const char* dest_dir) {
    DIR *d_dir, *dir;
    if ((d_dir = opendir(dest_dir)) == NULL) mkdir(dest_dir, 0755);
    closedir(d_dir);

    dir = opendir(source_dir);
    struct dirent* entry;
    char path[300];
 
    while ((entry = readdir(dir)) != NULL) {
        if (strcmp(entry->d_name, ".") == 0 || strcmp(entry->d_name, "..") == 0) {
            continue;
        }
        snprintf(path, sizeof(path), "%s/%s", source_dir, entry->d_name);
        if (entry->d_type == DT_DIR) {
            char dest_sub_dir[300];
            snprintf(dest_sub_dir, sizeof(dest_sub_dir), "%s/%s", dest_dir, entry->d_name);
            mkdir(dest_sub_dir, 0755);
            copy_dir(path, dest_sub_dir);
        } else {
            char dest_file[300];
            snprintf(dest_file, sizeof(dest_file), "%s/%s", dest_dir, entry->d_name);
            
            FILE* source_file = fopen(path, "rb");
            FILE* dest_file_ptr = fopen(dest_file, "wb");
 
            if (source_file == NULL){
                _log(NONE, __FILE__, "Failed to open %s", path);
                return;
            }
            if(dest_file_ptr == NULL){
                _log(NONE, __FILE__, "Failed to open %s", dest_file);
                return;
            }
 
            int ch;
            while ((ch = fgetc(source_file)) != EOF) {
                fputc(ch, dest_file_ptr);
            }
 
            fclose(source_file);
            fclose(dest_file_ptr);
        }
    }
 
    closedir(dir);
}

/* get the current path */
int get_path(char* path){
    char buffer[300];
    if (!getcwd(buffer, sizeof(buffer))) {
        return 0;
    }
    strcpy(path, buffer);
    return 1;
}

/* get the parent directory */
int get_parent_directory(char* current_path) {
    char* last_slash = strrchr(current_path, '/'); 
    if (last_slash == NULL) {
        // can not find parent path
        *current_path = '\0'; 
        return 0;
    } else if (last_slash == current_path) {
        // root path
        return 0;
    } else {
        *last_slash = '\0';
        return 1;
    }
}

/* check whether the directory is empty */
int is_directory_empty(const char *path) {
    DIR *dir;
    struct dirent *entry;
    int empty = 1;
    dir = opendir(path);
    if (dir == NULL) {
        return -1;
    }
    while ((entry = readdir(dir)) != NULL) {
        if (strcmp(entry->d_name, ".") == 0 || strcmp(entry->d_name, "..") == 0) {
            continue;
        }
        empty = 0;
        break;
    }
    closedir(dir);
    return empty;
}

/* find out the empty folder */
void find_empty_folder(char *path, Stack *s){
    DIR *dir;
    struct dirent *entry;
    if((dir = opendir(path)) == NULL) {
        return;
    }
    while ((entry = readdir(dir)) != NULL) {
        if (strcmp(entry->d_name, ".") == 0 || strcmp(entry->d_name, "..") == 0) {
            continue;
        }
        char fullpath[300];
        snprintf(fullpath, sizeof(fullpath), "%s/%s", path, entry->d_name);
        if (entry->d_type == DT_DIR){
            if(is_directory_empty(fullpath)){
                char* str = (char*)malloc(sizeof(char)*300);
                snprintf(str, sizeof(char)*300, "%s", fullpath);
                stack_push(s, str);
            }
            find_empty_folder(fullpath, s);
        }
    }
    closedir(dir);
}

/* delete the empty folder */
void delete_empty_folder(char *path){
    Stack s;
    init_stack(&s);
    find_empty_folder(path, &s);
    char parent[300];
    while(!is_stack_empty(&s)){
        char * tmp = (char*)stack_pop(&s);
        if(rmdir(tmp) != 0) {
            continue;  
        }
        get_parent_directory(tmp);
        strcpy(parent, tmp);
        free(tmp);
        while(strcmp(parent, path) != 0){
            if(is_directory_empty(parent)){
                if(rmdir(parent) != 0) {
                    break;  
                }
                get_parent_directory(parent);
            }else{
                break;
            }
        }
    }
    free_stack(&s);
}





